<!DOCTYPE html>



  


<html class="theme-next mist use-motion" lang="en">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Spring," />










<meta name="description" content="本文介绍 Spring AOP 的原理，主要包括静态代理与动态代理、JDK 动态代理与 CGLIB 动态代理的内容，最后以两个 Spring AOP 的示例收尾。">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring AOP 与代理">
<meta property="og:url" content="http://blog.eknown.cn/spring-aop%E4%B8%8E%E4%BB%A3%E7%90%86.html">
<meta property="og:site_name" content="空夜&#39;s Blog">
<meta property="og:description" content="本文介绍 Spring AOP 的原理，主要包括静态代理与动态代理、JDK 动态代理与 CGLIB 动态代理的内容，最后以两个 Spring AOP 的示例收尾。">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1590916137957.png">
<meta property="article:published_time" content="2020-05-30T16:00:00.000Z">
<meta property="article:modified_time" content="2020-06-13T23:34:07.010Z">
<meta property="article:author" content="空夜">
<meta property="article:tag" content="Spring">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1590916137957.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Mist',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: 'Author'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://blog.eknown.cn/spring-aop与代理.html"/>





  <title>Spring AOP 与代理 | 空夜's Blog</title>
  





  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?ec66ae5634d881676d66c21fdc6147a1";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>




<meta name="generator" content="Hexo 4.2.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="en">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">空夜's Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <h1 class="site-subtitle" itemprop="description">记录与分享 Java/Spring/SpringBoot 技术</h1>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            Home
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br />
            
            About
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            Tags
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
            
            Categories
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            Archives
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://blog.eknown.cn/spring-aop%E4%B8%8E%E4%BB%A3%E7%90%86.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="空夜">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/head.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="空夜's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">Spring AOP 与代理</h2>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">Posted on</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-05-31T00:00:00+08:00">
                2020-05-31
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">In</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Spring/" itemprop="url" rel="index">
                    <span itemprop="name">Spring</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/spring-aop%E4%B8%8E%E4%BB%A3%E7%90%86.html#comments" itemprop="discussionUrl">
                  <span class="post-comments-count valine-comment-count" data-xid="/spring-aop%E4%B8%8E%E4%BB%A3%E7%90%86.html" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          
          
             <span id="/spring-aop%E4%B8%8E%E4%BB%A3%E7%90%86.html" class="leancloud_visitors" data-flag-title="Spring AOP 与代理">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">Visitors&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          

          

          
              <div class="post-description">
                  本文介绍 Spring AOP 的原理，主要包括静态代理与动态代理、JDK 动态代理与 CGLIB 动态代理的内容，最后以两个 Spring AOP 的示例收尾。
              </div>
          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>SpringBoot 系列教程 - 源码地址：<a href="https://github.com/laolunsi/spring-boot-examples" target="_blank" rel="noopener">https://github.com/laolunsi/spring-boot-examples</a></p>
<hr>
<p>大家知道我现在还是一个 CRUD 崽，平时用 AOP 也是 CV 大法。最近痛定思痛，决定研究一下 Spring AOP 的原理。</p>
<p>这里写一篇文章总结一下。主要介绍 Java 中 AOP 的实现原理，最后以两个简单的示例来收尾。</p>
<hr>
<h2 id="一、AOP-的基本概念"><a href="#一、AOP-的基本概念" class="headerlink" title="一、AOP 的基本概念"></a>一、AOP 的基本概念</h2><h3 id="1-1-什么是-AOP"><a href="#1-1-什么是-AOP" class="headerlink" title="1.1 什么是 AOP"></a>1.1 什么是 AOP</h3><p>Aspect Oriented Programming，面向切面编程。</p>
<p>就跟我们说 OOP 是面向对象一样，AOP 是面向切面的。切面是分散在应用中的一个标准代码或功能。切面通常与实际的业务逻辑不同（例如，事务管理）。每个切面专注于一个特定的环切功能。</p>
<p>这里的切面呢，可以理解为横切。比如在所有的 DAO 层方法上加上一个同样的切面，功能是记录日志；又或者在某个接口上应用一个切面，作用是检查权限。</p>
<p>AOP 是基于<code>代理</code>来实现的。而代理又分为静态代理和动态代理。两者的区别在于代理类于何时生成。</p>
<p>下面我们讲讲代理是怎么回事？</p>
<hr>
<h3 id="1-2-代理与-Spring-AOP"><a href="#1-2-代理与-Spring-AOP" class="headerlink" title="1.2 代理与 Spring AOP"></a>1.2 代理与 Spring AOP</h3><p>代理分为静态代理和动态代理：</p>
<ul>
<li><p>静态代理：代理类在编译阶段生成，程序运行前就存在。包括：AspectJ 静态代理、JDK 静态代理</p>
</li>
<li><p>动态代理：代理类在程序运行时创建。包括：JDK 动态代理、CGLib 动态代理</p>
</li>
</ul>
<p>Spring AOP 原理：</p>
<ul>
<li><p>JDK Proxy： interface based</p>
</li>
<li><p>CGLib Proxy: class based</p>
</li>
</ul>
<p><img src="http://zfh-public-blog.oss-cn-beijing.aliyuncs.com/image-1590916137957.png" alt=""></p>
<p>Spring AOP 中默认使用 JDK 动态代理，通过反射获取被代理的类，这个类必须实现一个接口。如果目标类没有实现接口，就会默认使用 CGLIB Proxy 来动态生成代理目标类，后者是被代理类的子类。</p>
<p>可以通过获取代理对象并打印的方式来查看其类型（JDK Proxy 下是 com.sun.prxy, CGlib 下是子类.</p>
<p>AspectJ:  用特定的编译器和语法，在编译时增强，实现了静态代理技术。</p>
<h3 id="1-3-Spring-AOP-与-AspectJ-的区别"><a href="#1-3-Spring-AOP-与-AspectJ-的区别" class="headerlink" title="1.3 Spring AOP 与 AspectJ 的区别"></a>1.3 Spring AOP 与 AspectJ 的区别</h3><p>AspectJ 是一套完整的 AOP 解决方案，而 Spring AOP 并不是 —— 它只是在 Spring 框架下满足其使用要求的一个解决方法，比如 Spring AOP 仅支持对方法使用切面。</p>
<hr>
<h2 id="二、静态代理"><a href="#二、静态代理" class="headerlink" title="二、静态代理"></a>二、静态代理</h2><h3 id="2-1-AspectJ-静态代理"><a href="#2-1-AspectJ-静态代理" class="headerlink" title="2.1 AspectJ 静态代理"></a>2.1 AspectJ 静态代理</h3><p>基于特殊的编译器和语法。这里不多介绍了。</p>
<p>IDEA 下编译 AspectJ 可以参考这篇：<a href="https://blog.csdn.net/gavin_john/article/details/80156963" target="_blank" rel="noopener">https://blog.csdn.net/gavin_john/article/details/80156963</a></p>
<hr>
<h3 id="2-2-JDK-静态代理"><a href="#2-2-JDK-静态代理" class="headerlink" title="2.2 JDK 静态代理"></a>2.2 JDK 静态代理</h3><p>实际上是利用实现一个具体的代理类来调用业务类。代理类持有了一个业务类的引用。</p>
<p>更概况地说，JDK 静态代理体现的是一种设计模式。</p>
<p>缺点很明显，代码冗余，难以维护。</p>
<p>这里以 <code>借书</code> 和 <code>还书</code> 这两个行为来作为一个示例：</p>
<p>编写一个 BookService 接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">BookService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">borrow</span><span class="params">(String id, String userName)</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">reBack</span><span class="params">(String id, String userName)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>然后实现这个接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookServiceImpl</span> <span class="keyword">implements</span> <span class="title">BookService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">borrow</span><span class="params">(String id, String userName)</span> </span>&#123;</span><br><span class="line">        System.out.println(userName + <span class="string">" 借书："</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">reBack</span><span class="params">(String id, String userName)</span> </span>&#123;</span><br><span class="line">        System.out.println(userName + <span class="string">" 还书："</span> + id);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>下面我们来编写 <code>BookService</code> 的代理类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookProxy</span> <span class="keyword">implements</span> <span class="title">BookService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> BookServiceImpl bookService;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BookProxy</span><span class="params">(BookServiceImpl bookService)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.bookService = bookService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">borrow</span><span class="params">(String id, String userName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> res = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (check()) &#123;</span><br><span class="line">            res = bookService.borrow(id, userName);</span><br><span class="line">        &#125;</span><br><span class="line">        addLog();</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">reBack</span><span class="params">(String id, String userName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">boolean</span> res = <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (check()) &#123;</span><br><span class="line">            res = bookService.reBack(id, userName);</span><br><span class="line">        &#125;</span><br><span class="line">        addLog();</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">check</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"检查权限"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addLog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"操作完成"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>编写一个测试类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BookProxy proxy = <span class="keyword">new</span> BookProxy(<span class="keyword">new</span> BookServiceImpl());</span><br><span class="line">        proxy.borrow(<span class="string">"123"</span>, <span class="string">"eknown"</span>);</span><br><span class="line">        proxy.reBack(<span class="string">"234"</span>, <span class="string">"java"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>这里我们可以看到，JDK 静态代理就是说在原来的实现类上套一层 <code>代理</code>。它好像是体现了代理模式，但实际上并没有带来太多的好处。代码相当冗余，也不利于维护。</p>
<p>真正体现代理模式好处的还是动态代理，下面我们来看看动态代理的原理。</p>
<hr>
<h2 id="三、动态代理"><a href="#三、动态代理" class="headerlink" title="三、动态代理"></a>三、动态代理</h2><p>动态代理是程序运行时，由 JVM 根据反射等机制动态生成代理类的。</p>
<p>也就是说，程序运行前，我们仅仅定义了代理的规则，而不知道代理类具体长什么样，这不像上面的静态代理里，我们完整地定义了代理对象。</p>
<hr>
<h3 id="3-1-JDK-动态代理"><a href="#3-1-JDK-动态代理" class="headerlink" title="3.1 JDK 动态代理"></a>3.1 JDK 动态代理</h3><p>JDK 动态代理是基于接口的。</p>
<p>我们可以通过实现 <code>InvocationHandler</code>  接口来手动创建一个 JDK 代理类。</p>
<p>首先需要定义一个接口，让业务类和代理类都实现这个接口。</p>
<p>然后编写一个 InvocationHandler 接口的实现类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookProxy</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 被该代理类处理的业务类</span></span><br><span class="line">    <span class="keyword">private</span> BookService bookService;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">BookProxy</span><span class="params">(BookService bookService)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.bookService = bookService;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        Object res = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (check()) &#123;</span><br><span class="line">            <span class="comment">// 调用实际的 method，参数是 接口 + 参数</span></span><br><span class="line">            res = method.invoke(bookService, args);</span><br><span class="line">        &#125;</span><br><span class="line">        addLog();</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">check</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"检查权限"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addLog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"操作完成"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 创建被代理的实际业务类</span></span><br><span class="line">        BookServiceImpl bookServiceImpl = <span class="keyword">new</span> BookServiceImpl();</span><br><span class="line"></span><br><span class="line">        ClassLoader classLoader = bookServiceImpl.getClass().getClassLoader();</span><br><span class="line">        <span class="comment">// 获取所有的接口方法</span></span><br><span class="line">        Class[] interfaces = bookServiceImpl.getClass().getInterfaces();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构造 Handler</span></span><br><span class="line">        InvocationHandler invocationHandler = <span class="keyword">new</span> BookProxy(bookServiceImpl);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建代理</span></span><br><span class="line">        Object obj = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);</span><br><span class="line">        BookService bookService = (BookService) obj;</span><br><span class="line">        bookService.borrow(<span class="string">"abc"</span>, <span class="string">"eknown"</span>);</span><br><span class="line">        bookService.reBack(<span class="string">"c23"</span>, <span class="string">"py"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<hr>
<h3 id="3-2-CGLIB-动态代理"><a href="#3-2-CGLIB-动态代理" class="headerlink" title="3.2 CGLIB 动态代理"></a>3.2 CGLIB 动态代理</h3><p>CGLIB 代理的原理是：让代理类继承业务类（也就自动拥有了业务类的所有非 final 的 public 方法）</p>
<p>我们这里手动编写一个 CGLIB 的代理试试看。</p>
<p>首先我们有一个 BookServiceImpl 业务类，这个业务类可以实现接口，也可以就是单纯的一个业务类。</p>
<p>然后我们定义一个 BookCglibProxy 类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookCglibProxy</span> <span class="keyword">implements</span> <span class="title">MethodInterceptor</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">intercept</span><span class="params">(Object o, Method method, Object[] objects, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable </span>&#123;</span><br><span class="line">        check();</span><br><span class="line">        <span class="comment">// 调用实际的 method</span></span><br><span class="line">        Object obj = methodProxy.invokeSuper(o, objects);</span><br><span class="line">        addLog();</span><br><span class="line">        <span class="keyword">return</span> obj;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">check</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"检查权限"</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">addLog</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"操作完成"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>测试类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CglibTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        BookServiceImpl bookServiceImpl = <span class="keyword">new</span> BookServiceImpl();</span><br><span class="line"></span><br><span class="line">        BookCglibProxy proxy = <span class="keyword">new</span> BookCglibProxy();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// cjlib 中的增强器，用于创建动态代理（被代理类的子类）</span></span><br><span class="line">        Enhancer enhancer = <span class="keyword">new</span> Enhancer();</span><br><span class="line">        <span class="comment">// 设置要被代理的类</span></span><br><span class="line">        enhancer.setSuperclass(bookServiceImpl.getClass());</span><br><span class="line">        <span class="comment">// 设置回调</span></span><br><span class="line">        enhancer.setCallback(proxy);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 强转成父类</span></span><br><span class="line">        BookServiceImpl proxyResult = (BookServiceImpl) enhancer.create();</span><br><span class="line">        proxyResult.borrow(<span class="string">"12333"</span>, <span class="string">"ye"</span>);</span><br><span class="line">        proxyResult.reBack(<span class="string">"123"</span>, <span class="string">"fe"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>在第一节我们提到过 Spring AOP 是基于 JDK 动态代理和 CGLIB 动态代理的。下面我们来 Spring AOP 的一些基本案例。</p>
<hr>
<h2 id="四、Spring-AOP-实例"><a href="#四、Spring-AOP-实例" class="headerlink" title="四、Spring AOP 实例"></a>四、Spring AOP 实例</h2><p>AOP 中一些概念词汇，通过这些词汇，我们可以对 AOP 有更高一层的抽象。</p>
<ul>
<li>Aspect - 切面，分散在应用中的一个标准代码或功能。切面通常与实际的业务逻辑不同（例如，事务管理）。每个切面专注于一个特定的环切功能。</li>
<li>Joinpoint - 连接点，是程序执行过程中的特定点，比如方法执行、构造器调用、字段赋值</li>
<li>Advice - 通知，切面在某个连接点采取的操作。Advice 有 5 种类型。</li>
<li>Pointcut - 切入点，一个匹配连接点的正则表达式。每当连接点匹配了一个切入点时，一个特定的通知就会被执行。</li>
<li>Weaving - 织入，指的是将切面和目标对象连接起来以创建代理对象的过程。</li>
</ul>
<p>Spring AOP 有两种实现方式：基于 XML 或基于注解。更流行、更方便的是后者。（阿 sir，不会还有人用 XML 来做 Bean 的配置文件吧？）</p>
<hr>
<h3 id="4-1-基于-XML-的实例"><a href="#4-1-基于-XML-的实例" class="headerlink" title="4.1 基于 XML 的实例"></a>4.1 基于 XML 的实例</h3><p>首先定义一下接口和实现类（没有注解的！）。再编写一个代理类：</p>
<p>这里的代理类方法以 JoinPoint 为参数即可：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookAspect</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">checkUser</span><span class="params">(JoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"-----before-----"</span>);</span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line">        <span class="keyword">for</span> (Object arg : args) &#123;</span><br><span class="line">            System.out.println(arg);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"检查用户权限..."</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveLog</span><span class="params">(JoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"-----after-----"</span>);</span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line">        <span class="keyword">for</span> (Object arg : args) &#123;</span><br><span class="line">            System.out.println(arg);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"请求完毕，记录日志..."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>然后编写 Spring 的配置文件：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">"http://www.springframework.org/schema/beans"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:aop</span>=<span class="string">"http://www.springframework.org/schema/aop"</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="tag"><span class="string">        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd</span></span></span><br><span class="line"><span class="tag"><span class="string">        http://www.springframework.org/schema/aop</span></span></span><br><span class="line"><span class="tag"><span class="string">        http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 定义 bean --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"bookService"</span> <span class="attr">class</span>=<span class="string">"com.example.springaopdemo.basicxml.BookServiceImpl"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"bookAspect"</span> <span class="attr">class</span>=<span class="string">"com.example.springaopdemo.basicxml.BookAspect"</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">aop:config</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 这是定义一个切面，切面是切点和通知的集合--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">aop:aspect</span> <span class="attr">id</span>=<span class="string">"do"</span> <span class="attr">ref</span>=<span class="string">"bookAspect"</span>&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 定义切点 ，后面是 expression 语言，表示包括该接口中定义的所有方法都会被执行 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">"point"</span> <span class="attr">expression</span>=<span class="string">"execution(* com.example.springaopdemo.basicxml.BookService.*(..))"</span> /&gt;</span></span><br><span class="line">            <span class="comment">&lt;!-- 定义通知 --&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">aop:before</span> <span class="attr">method</span>=<span class="string">"checkUser"</span> <span class="attr">pointcut-ref</span>=<span class="string">"point"</span> /&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">aop:after</span> <span class="attr">method</span>=<span class="string">"saveLog"</span> <span class="attr">pointcut-ref</span>=<span class="string">"point"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">aop:aspect</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">aop:config</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>



<p>运行测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AopXMLTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        ApplicationContext context = <span class="keyword">new</span> ClassPathXmlApplicationContext(<span class="string">"SpringAop.xml"</span>);</span><br><span class="line"></span><br><span class="line">        BookService bookService = context.getBean(<span class="string">"bookService"</span>, BookService<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        bookService.borrow(<span class="string">"123"</span>, <span class="string">"eknown"</span>);</span><br><span class="line">        bookService.reback(<span class="string">"123"</span>, <span class="string">"eknown"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>基于 XML 配置的 Spring 现在已经很少使用了。下面我们来看看如何基于注解使用 Spring AOP</p>
<hr>
<h3 id="4-2-基于注解的实例"><a href="#4-2-基于注解的实例" class="headerlink" title="4.2 基于注解的实例"></a>4.2 基于注解的实例</h3><p>这里以一个使用 SpringBoot 框架的 Web 项目作为简单的实例。</p>
<p>首先创建一个 SpringBoot 项目，写好 Controller、Service、DAO 层的基本类。（示例源码中没有使用 Mybatis 等持久层框架，而是用 Map 来模拟数据的存取）</p>
<p>下面我们针对 UserService 接口类，添加切面。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Aspect</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserAspect</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Before</span>(value = <span class="string">"execution(* com.example.springaopdemo.boot.UserService.*(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">checkUser</span><span class="params">(JoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"-----before-----"</span>);</span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line">        <span class="keyword">for</span> (Object arg : args) &#123;</span><br><span class="line">            System.out.println(arg);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"检查..."</span> + point);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@After</span>(value = <span class="string">"execution(* com.example.springaopdemo.boot.UserService.*(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveLog</span><span class="params">(JoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"-----after-----"</span>);</span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line">        <span class="keyword">for</span> (Object arg : args) &#123;</span><br><span class="line">            System.out.println(arg);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 这里可以使用 point.getTarget() 获取到切面对应的 bean</span></span><br><span class="line">        <span class="comment">//Object target = point.getTarget();</span></span><br><span class="line">        <span class="comment">//UserService userService = (UserService) target;</span></span><br><span class="line">        <span class="comment">//List&lt;User&gt; userList = userService.findAll();</span></span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"请求完毕，记录日志..."</span> + point);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Around</span>(value = <span class="string">"execution(* com.example.springaopdemo.boot.UserService.save(..))"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">saveAround</span><span class="params">(ProceedingJoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"around-before"</span>);</span><br><span class="line">        Object obj = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            obj = point.proceed();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">            throwable.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"around-after"</span>);</span><br><span class="line">        <span class="keyword">return</span> obj;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>示例中使用了 @Before/@After/@Aroud 三个注解，value 中使用切点表达式，分别匹配了 UserService 接口的所有方法和单个 save 方法。</p>
<p>我们还可以通过切点表达式匹配自定义的注解，比如实现一个 UserMonitor 注解，然后定义其切点方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> UserMonitor &#123;</span><br><span class="line"></span><br><span class="line">    <span class="function">String <span class="title">value</span><span class="params">()</span> <span class="keyword">default</span> ""</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">roleLimit</span><span class="params">()</span> <span class="keyword">default</span> 0</span>;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>切点：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Around</span>(<span class="string">"@annotation(com.example.springaopdemo.boot.UserMonitor)"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">userRolePointCut</span><span class="params">(ProceedingJoinPoint point)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"检查用户权限..."</span>);</span><br><span class="line">    </span><br><span class="line">      	<span class="comment">// 获取参数</span></span><br><span class="line">        Object[] args = point.getArgs();</span><br><span class="line">        Class&lt;?&gt;[] argTypes = <span class="keyword">new</span> Class[point.getArgs().length];</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; args.length; i++) &#123;</span><br><span class="line">            argTypes[i] = args[i].getClass();</span><br><span class="line">        &#125;</span><br><span class="line">      </span><br><span class="line">        <span class="comment">// 获取方法</span></span><br><span class="line">        Method method = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            method = point.getTarget().getClass()</span><br><span class="line">                    .getMethod(point.getSignature().getName(), argTypes);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchMethodException | SecurityException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取方法上的该注解，之后可以根据注解中的值进行一些操作，比如判定是否具有权限</span></span><br><span class="line">        UserMonitor monitor = method.getAnnotation(UserMonitor<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line">        System.out.println(monitor);</span><br><span class="line">        Object obj = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            obj = point.proceed();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable throwable) &#123;</span><br><span class="line">            throwable.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> obj;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<hr>
<p>参考资料：</p>
<ul>
<li><p>什么是面向切面编程AOP？<a href="https://www.zhihu.com/question/24863332" target="_blank" rel="noopener">https://www.zhihu.com/question/24863332</a> </p>
</li>
<li><p>一起来谈谈 Spring AOP！: <a href="https://juejin.im/post/5aa7818af265da23844040c6" target="_blank" rel="noopener">https://juejin.im/post/5aa7818af265da23844040c6</a></p>
<blockquote>
<p>一个简单的 AOP 示例，从 XML 到注解。</p>
</blockquote>
</li>
<li><p>Spring AOP的实现原理: <a href="https://juejin.im/post/5b67b9a86fb9a04fc71afeb4" target="_blank" rel="noopener">https://juejin.im/post/5b67b9a86fb9a04fc71afeb4</a></p>
<blockquote>
<p>也是一篇简单的使用示例文章。</p>
</blockquote>
</li>
<li><p>从代理机制到Spring AOP: <a href="https://juejin.im/post/5b90e648f265da0aea695672" target="_blank" rel="noopener">https://juejin.im/post/5b90e648f265da0aea695672</a></p>
<blockquote>
<p>这篇讲得非常好，深刻讲述了各种代理的原理和示例。强烈推荐看看这一篇！</p>
</blockquote>
</li>
</ul>
<ul>
<li><p>比较 Spring AOP 和AspectJ：<a href="https://www.baeldung.com/spring-aop-vs-aspectj" target="_blank" rel="noopener">https://www.baeldung.com/spring-aop-vs-aspectj</a></p>
<blockquote>
<p>一篇有关 Spring AOP 和 AspectJ 的英文资料</p>
</blockquote>
</li>
<li><p>三歪 - Spring【AOP模块】就是这么简单: [<a href="https://juejin.im/post/5aa8edf06fb9a028d0432584]" target="_blank" rel="noopener">https://juejin.im/post/5aa8edf06fb9a028d0432584]</a>(</p>
</li>
</ul>

      
    </div>
    
    
    

    
      <div>
        <div id="wechat_subscriber" style="display: block; padding: 10px 0; margin: 20px auto; width: 100%; text-align: center">
    <img id="wechat_subscriber_qcode" src="/uploads/javaapes.jpg" alt="空夜 wechat" style="width: 200px; max-width: 100%;"/>
    <div>欢迎扫一扫上面的微信公众号，第一时间订阅我的博客！</div>
</div>

      </div>
    

    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Spring/" rel="tag"># Spring</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/spring-boot-cache.html" rel="next" title="SpringBoot Cache 技术实战">
                <i class="fa fa-chevron-left"></i> SpringBoot Cache 技术实战
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/hexo%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99%E6%8C%87%E5%8D%97.html" rel="prev" title="hexo 博客建站指南">
                hexo 博客建站指南 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  
    <div class="comments" id="comments">
    </div>
  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            Table of Contents
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            Overview
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/images/head.jpg"
                alt="空夜" />
            
              <p class="site-author-name" itemprop="name">空夜</p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/%7C%7C%20archive">
              
                  <span class="site-state-item-count">8</span>
                  <span class="site-state-item-name">posts</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">5</span>
                  <span class="site-state-item-name">categories</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">5</span>
                  <span class="site-state-item-name">tags</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/laolunsi" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
            </div>
          

          
          

          
          
            <div class="links-of-blogroll motion-element links-of-blogroll-block">
              <div class="links-of-blogroll-title">
                <i class="fa  fa-fw fa-link"></i>
                Links
              </div>
              <ul class="links-of-blogroll-list">
                
                  <li class="links-of-blogroll-item">
                    <a href="http://eknown.cn/" title="主站" target="_blank">主站</a>
                  </li>
                
              </ul>
            </div>
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#一、AOP-的基本概念"><span class="nav-number">1.</span> <span class="nav-text">一、AOP 的基本概念</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-什么是-AOP"><span class="nav-number">1.1.</span> <span class="nav-text">1.1 什么是 AOP</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-代理与-Spring-AOP"><span class="nav-number">1.2.</span> <span class="nav-text">1.2 代理与 Spring AOP</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-Spring-AOP-与-AspectJ-的区别"><span class="nav-number">1.3.</span> <span class="nav-text">1.3 Spring AOP 与 AspectJ 的区别</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#二、静态代理"><span class="nav-number">2.</span> <span class="nav-text">二、静态代理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-AspectJ-静态代理"><span class="nav-number">2.1.</span> <span class="nav-text">2.1 AspectJ 静态代理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-JDK-静态代理"><span class="nav-number">2.2.</span> <span class="nav-text">2.2 JDK 静态代理</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#三、动态代理"><span class="nav-number">3.</span> <span class="nav-text">三、动态代理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-JDK-动态代理"><span class="nav-number">3.1.</span> <span class="nav-text">3.1 JDK 动态代理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-CGLIB-动态代理"><span class="nav-number">3.2.</span> <span class="nav-text">3.2 CGLIB 动态代理</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#四、Spring-AOP-实例"><span class="nav-number">4.</span> <span class="nav-text">四、Spring AOP 实例</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-基于-XML-的实例"><span class="nav-number">4.1.</span> <span class="nav-text">4.1 基于 XML 的实例</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-基于注解的实例"><span class="nav-number">4.2.</span> <span class="nav-text">4.2 基于注解的实例</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">空夜</span>

  
</div>


  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Mist</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  










  <script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
  <script src="//unpkg.com/valine/dist/Valine.min.js"></script>
  
  <script type="text/javascript">
    var GUEST = ['nick','mail','link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item=>{
      return GUEST.indexOf(item)>-1;
    });
    new Valine({
        el: '#comments' ,
        verify: false,
        notify: false,
        appId: '44HlYzgCKimM8L4LEvP2Efue-gzGzoHsz',
        appKey: 'vPbPmSOtHHap8f2JnCGtrTbG',
        placeholder: 'Just go go',
        avatar:'mm',
        guest_info:guest,
        pageSize:'10' || 10,
    });
  </script>



  





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("44HlYzgCKimM8L4LEvP2Efue-gzGzoHsz", "vPbPmSOtHHap8f2JnCGtrTbG");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  

  
  

  

  

  

</body>
</html>
